#pragma once

#include <windows.h>
#include <process.h>
#include "Functor.h"

#define MAXWAITEXITTIME 30000

namespace TinyCC
{
	class CTinyThread
	{
	private:
		CTinyThread(const CTinyThread&):m_pFunctor(NULL),m_pParam(NULL),m_hThread(NULL),m_ThreadId(0){}
	public:
		template <typename Fun >		
		CTinyThread (const Fun & threadfunc, void* param):m_pParam( param )	
		{
			m_pFunctor = new Loki::Functor<void, LOKI_TYPELIST_1(void*) >(threadfunc);
			m_hThread = (HANDLE)_beginthreadex( 
				NULL,
				0,
				threadFunc, 
				static_cast<void*>( this ),
				0,
				&m_ThreadId);
		}

		template <typename PointerToObj, typename PointerToMemberFunc >
		CTinyThread(const PointerToObj& pObj, PointerToMemberFunc pMemberFunc, void* param):m_pParam( param )	
		{
			m_pFunctor = new Loki::Functor<void, LOKI_TYPELIST_1(void*) >(pObj, pMemberFunc);
			m_hThread = (HANDLE)_beginthreadex( 
				NULL,
				0,
				threadFunc, 
				static_cast<void*>( this ),
				0,
				&m_ThreadId);
		}
		~CTinyThread(void)
		{
			if( m_pFunctor != NULL )
			{
				delete m_pFunctor;
				m_pFunctor = NULL;
			}
			if( m_hThread!=NULL )
			{
				normalExit();
			}
		}

		bool isEqual(const CTinyThread& tinyThread2) const
		{
			if( m_hThread == NULL )
				return false;
			return ( m_ThreadId == tinyThread2.m_ThreadId ) && ( m_hThread == tinyThread2.m_hThread );
		}

		//normal thread exit.
		inline void normalExit()
		{
			if( m_hThread != NULL )
			{
				WaitForSingleObject( m_hThread, MAXWAITEXITTIME);

				//if thread can't exit in 30 second,forceExit will be invoked!
				DWORD exitcode=0;
				GetExitCodeThread(m_hThread,&exitcode);
				if(exitcode==STILL_ACTIVE)
					forceExit();
				else
					CloseHandle( m_hThread );
			}
		}

		//Force to terminate the running thread.
		inline bool forceExit()
		{
			if( m_hThread != NULL )
			{
				TerminateThread( m_hThread, 0 );
				CloseHandle( m_hThread );
			}
			return false;
		}

		unsigned int getThreadId() const{	return m_ThreadId;	}
		HANDLE getThreadHandle() const{	return m_hThread;	}

	private:
		static unsigned int WINAPI threadFunc( void* p )
		{
			CTinyThread* pThread = (CTinyThread*)p;

			if( pThread->m_pFunctor != NULL )
				(*(pThread->m_pFunctor))(pThread->m_pParam);

			return 0;
		}

		Loki::Functor<void, LOKI_TYPELIST_1(void*)  >* m_pFunctor;
		void* m_pParam;
		HANDLE m_hThread;
		unsigned int m_ThreadId;
	};
}
